package com.li.onjava8.concurrent.under.sync;

import com.li.onjava8.concurrent.under.share.EvenChecker;
import com.li.onjava8.concurrent.under.share.IntGenerator;
import com.li.onjava8.concurrent.utils.Nap;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * java.util.concurrent 库包含在 java.util.concurrent.locks 中定义的显示互斥锁机制。 必须显式地创建，
 * 锁定和解锁 Lock 对象，因此它产出的代码没有内置 synchronized 关键字那么优雅。然而，它在解决某
 * 些类型的问题时更加灵活。
 *
 * 当你使用 Lock 对象时，使用下面显示的习惯用法很重要：在调用 Lock() 之后，你必
 * 须放置 try-finally 语句，该语句在 finally 子句中带有 unlock() 方法 - 这是确保锁总是被释放的惟一
 * 方法。注意，return 语句必须出现在 try 子句中，以确保 unlock() 不会过早发生并将数据暴露给第二个
 * 任务。
 */
public class MutexEvenProducer extends IntGenerator {

    private int currentEventValue = 0;
    private Lock lock = new ReentrantLock();

    /**
     * 尽管 try-finally 比起使用 synchronized 关键字需要用得更多代码，但它也代表了显式锁对象的优势之
     * 一。如果使用 synchronized 关键字失败，就会抛出异常，但是你没有机会进行任何清理以保持系统处
     * 于良好状态。而使用显式锁对象，可以使用 finally 子句在系统中维护适当的状态。
     *
     * 一般来说，当你使用 synchronized 的时候，需要编写的代码更少，并且用户出错的机会也大大减少，
     * 因此通常只在解决特殊问题时使用显式锁对象。
     */
    @Override
    public int next() {
        lock.lock();
        try {
            ++currentEventValue;
            new Nap(0.01);
            ++currentEventValue;
            return currentEventValue;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        EvenChecker.test(new MutexEvenProducer());
    }
}
